package hnet

import (
	"fmt"
	"haruhi/hiface"
	"haruhi/utils"
	"strconv"
)

type MsgHandle struct {
	Apis           map[uint32]hiface.IRouter // 存放每个 MsgId 所对应的处理方法的map
	WorkerPoolSize uint32                    // 业务工作 worker 池的数量
	TaskQueue      []chan hiface.IRequest    // worker 负责取任务的消息队列
}

func NewMsgHandle() *MsgHandle {
	return &MsgHandle{
		Apis:           make(map[uint32]hiface.IRouter),
		WorkerPoolSize: utils.GlobalObject.WorkerPoolSize,
		TaskQueue:      make([]chan hiface.IRequest, utils.GlobalObject.WorkerPoolSize),
	}
}

// 以非阻塞方式处理消息
func (mh *MsgHandle) DoMsgHandler(request hiface.IRequest) {
	handler, ok := mh.Apis[request.GetMsgID()]
	if !ok {
		fmt.Println("api msgId = ", request.GetMsgID(), " is not found!")
		return
	}

	// 执行方法
	handler.PreHandle(request)
	handler.Handle(request)
	handler.PostHandle(request)
}

// 为消息添加具体的处理逻辑
func (mh *MsgHandle) AddRouter(msgId uint32, router hiface.IRouter) {
	// 1.判断当前msg绑定的api处理方法是否存在
	if _, ok := mh.Apis[msgId]; ok {
		panic("repeated api, msgId = " + strconv.Itoa(int(msgId)))
	}
	// 2.添加 msg 与 api 的绑定关系
	mh.Apis[msgId] = router
	fmt.Println("add api msgId = ", msgId)
}

func (mh *MsgHandle) StartOneWorker(workerID int, taskQueue chan hiface.IRequest)  {
	fmt.Println("Worker ID = ", workerID, " is started.")
	// 不断的等待队列中的消息
	for {
		select {
			// 有消息则去除队列的 Request, 并执行绑定的业务方法
		case request := <- taskQueue:
			mh.DoMsgHandler(request)
		}
	}
}

func (mh *MsgHandle) StartWorkerPool() {
	// 遍历所有需要启动worker的数量，依次启动
	for i:= 0; i < int(mh.WorkerPoolSize); i++ {
		// 一个 worker 被启动
		// 给当前 worker 对应的任务队列开辟空间
		mh.TaskQueue[i] = make(chan hiface.IRequest, utils.GlobalObject.MaxWorkerTaskLen)
		// 启动当前 worker , 阻塞的等待对应的任务队列是否有消息传来
		go mh.StartOneWorker(i, mh.TaskQueue[i])
	}
}

// 将消息交给 TaskQueue, 由 worker 进行处理
func (mh *MsgHandle) SendMsgToTaskQueue(request hiface.IRequest)  {
	// 根据 ConnID　来分配当前的连接应该由哪个　worker 负责处理
	// 轮询的平均分配法则

	// 得到需要处理此条连接的WorkerID
	workerID := request.GetConnection().GetConnID() % mh.WorkerPoolSize
	fmt.Println("Add ConnID = ", request.GetConnection().GetConnID(), " request msgID = ", request.GetMsgID(), " to workerID = ", workerID)
	//将请求消息发送给任务队列
	mh.TaskQueue[workerID] <- request
}